package br.poli.ecomp.sma.massim.arch;

import jason.JasonException;
import jason.ReceiverNotFoundException;
import jason.RevisionFailedException;
import jason.asSemantics.ActionExec;
import jason.asSemantics.Agent;
import jason.asSemantics.Message;
import jason.asSyntax.Atom;
import jason.asSyntax.Literal;
import jason.environment.grid.Location;
import jason.mas2j.ClassParameters;
import jason.runtime.Settings;

import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;


public class EcompArch extends IdentifyCrashed {

	public static Atom aOBSTACLE = new Atom("obstacle");
	public static Atom aENEMY = new Atom("enemy");
	public static Atom aENEMYCORRAL = new Atom("enemycorral");
	public static Atom aALLY = new Atom("ally");
	public static Atom aEMPTY = new Atom("empty");

	public static Atom aCOW = new Atom("cow");
	
	public static Atom aFENCE = new Atom("fence");
	public static Atom aNOFENCE = new Atom("no_fence");
	public static Atom aSWITCH = new Atom("switch");

	private Logger logger;

    private ServerProxyThread proxy;
    private ActionThread actionThread;

	private String simId = null;
	private int steps;
	
	private String opponent;

	private int simStep  = 0;

	private boolean playing = false;
	
    private Location lo1 = new Location(-1,-1), // last locations of the agent
                     lo2 = new Location(-1,-1),
                     lo3 = new Location(-1,-1),
                     lo4 = new Location(-1,-1),
                     lo5 = new Location(-1,-1),
                     lo6 = new Location(-1,-1),
                     lo7 = new Location(-1,-1);

	@Override
	public void initAg(String agClass, ClassParameters bbPars, String asSrc, Settings stts)
			throws JasonException {
		super.initAg(agClass, bbPars, asSrc, stts);

		logger = getTS().getLogger();

		// Obtem as configura��es do agente:
		String username = stts.getUserParameter("username");
        if (username.startsWith("\"")) username = username.substring(1,username.length()-1);
        String password = stts.getUserParameter("password");
        if (password.startsWith("\"")) password = password.substring(1,password.length()-1);

		// Obtem as configura��es do servidor:
        String host = stts.getUserParameter("host");
		int port = Integer.parseInt(stts.getUserParameter("port"));

		//TODO logger.setLevel(Level.FINEST);
		
		// Cria e inicia os Threads do agente:
		proxy = new ServerProxyThread(this, logger, host, port, username, password);
        actionThread = new ActionThread(this, proxy, logger);
        
        actionThread.start();
        proxy.start();
	}

	void loggedIn() {
		logger.info("---------------- login ----------------");
	}

	void setSimId(String id) {
		this.simId = id;
    }
	
	void gsizePerceived(int w, int h) throws RevisionFailedException {
        Literal gsizeLiteral = Literal.parseLiteral("gsize("+w+","+h+")");
        getTS().getAg().addBel(gsizeLiteral);
	}

	void stepsPerceived(int steps) throws RevisionFailedException {
		Literal stepsLiteral = Literal.parseLiteral("steps("+steps+")");
        getTS().getAg().addBel(stepsLiteral);

        this.steps = steps;

        playing = true;
	}
	
	void opponentPerceived(String opponent) throws RevisionFailedException {
        Literal opponentLiteral = Literal.parseLiteral("opponent(" + opponent + ")");
        getTS().getAg().addBel(opponentLiteral);
        
        this.opponent = opponent;
	}
	
	void corralPerceived(Location loc, Location loc2) throws RevisionFailedException {
        Literal opponentLiteral = Literal.parseLiteral("corral(" + loc.x + "," + loc.y + "," + loc2.x + "," + loc2.y + ")");
        getTS().getAg().addBel(opponentLiteral);
	}
	
	void addRestartGoal() {
//		logger.info("** Adding restart for " + getAgName());
//		
//		try {
//			getTS().getC().addAchvGoal(new LiteralImpl("restart"), Intention.EmptyInt);
//        	
//        	lo2 = new Location(-1,-1); // to not restart again in the next cycle
//    	} catch (Exception e) {
//    		logger.log(Level.SEVERE, "Error restarting agent: " + e);
//    	}
	}
	
    void stepPerceived(int step, Literal pos, List<Literal> percepts, long deadline)
    		throws RevisionFailedException {
		logger.info(getAgName() + " ---> Perception");

    	Agent ag = getTS().getAg();

        ag.delBel(Literal.parseLiteral("step(_)"));
        this.simStep = step;
        ag.addBel(Literal.parseLiteral("step(" + simStep + ")"));
        
        this.setMaxCycleTime(deadline);

        ag.delBel(Literal.parseLiteral("pos(_,_,_)"));
        ag.addBel(pos);
        
    	for (Literal literal : percepts) {
        	if (literal.getFunctor().equals("cow"))
        		ag.addBel(literal);
		}

		actionThread.newCycle(this.simStep);
	}

	void simulationEndPerceived(String score, String result) {
        logger.info("End of simulation :" + score + " - " + result);
	}
	
	
	// ACTION
	
	// Executes the action <i>action</i> and, when finished,
	// add it back in feedback actions.
	public void act(ActionExec action, List<ActionExec> feedback) {
		logger.info(getAgName() + " ---> Act: " + action.getActionTerm());
		
		if (action.getActionTerm().getFunctor().equals("moveTo")) {
			actionThread.addAction(action);
		}
		
		//super.act(action, feedback);
	}


	// Communication with other agents
	
	@Override
	public void checkMail() {
		super.checkMail();
		
		Iterator<Message> it = getTS().getC().getMailBox().iterator();
		while (it.hasNext()) {
			Message msg  = it.next();
			if (!msg.getReceiver().equals(getAgName())) continue;

			logger.log(Level.WARNING, "Mail not verified! Message: " + msg);
			//TODO: checkMail
		}
	}
	
	@Override
	public void broadcast(Message m) throws Exception {
		String basename = getAgName().replaceAll("[0-9]*$", "");
		int agId = (Integer.parseInt(getAgName().replace(basename, ""))) - 1;

		for (int i=0; i < 20; i++) {
    		if (i == agId) continue;

			Message msg = new Message(m);
			msg.setReceiver(basename + (i + 1));

			// try to send 10 times.
			for (int t = 0; t < 10; t++) {
				try {
					sendMsg(msg);
					break; // msg sent with success
				} catch (ReceiverNotFoundException e) {
					// wait to try again
					Thread.sleep(500);
				}
			}
		}
	}


	// Crash and Fix

	@Override
	public boolean isCrashed() {
	    return playing && super.isCrashed();
	}

    /**
     * User defined approach to fix the agent thread.
     */
    protected boolean fix3() throws Exception {
    	//TODO fix3
        return false;
    }    

}
